#include <stdio.h>
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_random.h"

#include "ssd1306.h"
//#include "font8x8_basic.h"

/*
 You have to set this config value with menuconfig
 CONFIG_INTERFACE

 for i2c
 CONFIG_MODEL
 CONFIG_SDA_GPIO
 CONFIG_SCL_GPIO
 CONFIG_RESET_GPIO

 for SPI
 CONFIG_CS_GPIO
 CONFIG_DC_GPIO
 CONFIG_RESET_GPIO
*/

#define TAG "SSD1306"

// https://www.vecteezy.com/vector-art/28199306-clouds-icon-vector-illustration
uint8_t cloud11[] = {
// 'cloud11', 64x32px
0xff, 0xff, 0xff, 0xff, 0xe0, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x1f, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xfe, 0x07, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x3f, 0xc3, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xf8, 0xff, 0xe3, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0xff, 0xf1, 0xff, 0xff, 
0xff, 0xff, 0xe0, 0x01, 0xff, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xc3, 0xfb, 0xff, 0xfc, 0xff, 0xff, 
0xff, 0xff, 0x8f, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0xff, 0x9f, 0xff, 0xff, 0xfc, 0xff, 0xff, 
0xff, 0xff, 0x1f, 0xff, 0xff, 0xfc, 0x7f, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xfc, 0x0f, 0xff, 
0xff, 0xfe, 0x3f, 0xff, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xf8, 0x3f, 0xff, 0xff, 0xff, 0xe3, 0xff, 
0xff, 0xf0, 0x7f, 0xff, 0xff, 0xff, 0xf1, 0xff, 0xff, 0xe3, 0xff, 0xff, 0xff, 0xff, 0xf8, 0xff, 
0xff, 0xc7, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0x8f, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x7f, 
0xff, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x7f, 
0xff, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x7f, 
0xff, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x7f, 
0xff, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x7f, 0xff, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff, 
0xff, 0x8f, 0xff, 0xff, 0xff, 0xff, 0xf8, 0xff, 0xff, 0xc7, 0xff, 0xff, 0xff, 0xff, 0xf1, 0xff, 
0xff, 0xe1, 0xff, 0xff, 0xff, 0xff, 0xc3, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 
0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};

uint8_t cloud12[] = {
// 'cloud12', 64x32px
0xff, 0xff, 0xff, 0xff, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xfc, 0x3c, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0xff, 0x1f, 0xff, 0xff, 
0xff, 0xff, 0xf8, 0x01, 0xff, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x83, 0xff, 0xcf, 0xff, 0xff, 
0xff, 0xff, 0xc7, 0xf3, 0xff, 0xcf, 0xff, 0xff, 0xff, 0xff, 0xcf, 0xff, 0xff, 0xcf, 0xff, 0xff, 
0xff, 0xff, 0x9f, 0xff, 0xff, 0xef, 0xff, 0xff, 0xff, 0xff, 0x9f, 0xff, 0xff, 0xc7, 0xff, 0xff, 
0xff, 0xff, 0x3f, 0xff, 0xff, 0xc0, 0xff, 0xff, 0xff, 0xfe, 0x3f, 0xff, 0xff, 0xf8, 0x7f, 0xff, 
0xff, 0xfc, 0x7f, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xf9, 0xff, 0xff, 0xff, 0xff, 0x9f, 0xff, 
0xff, 0xf3, 0xff, 0xff, 0xff, 0xff, 0x9f, 0xff, 0xff, 0xf3, 0xff, 0xff, 0xff, 0xff, 0xcf, 0xff, 
0xff, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xcf, 0xff, 0xff, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xcf, 0xff, 
0xff, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xcf, 0xff, 0xff, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xcf, 0xff, 
0xff, 0xf3, 0xff, 0xff, 0xff, 0xff, 0xcf, 0xff, 0xff, 0xf3, 0xff, 0xff, 0xff, 0xff, 0x9f, 0xff, 
0xff, 0xf9, 0xff, 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0x7f, 0xff, 0xff, 0xfc, 0x3f, 0xff, 
0xff, 0xfe, 0x0f, 0xff, 0xff, 0xc0, 0x7f, 0xff, 0xff, 0xff, 0x0f, 0xff, 0xff, 0x81, 0xff, 0xff, 
0xff, 0xff, 0xe7, 0xf9, 0xff, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xf1, 0xf0, 0x7f, 0x3f, 0xff, 0xff, 
0xff, 0xff, 0xf8, 0x02, 0x1c, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0f, 0x00, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};

uint8_t cloud13[] = {
// 'cloud13', 64x32px
0xff, 0xff, 0xff, 0xff, 0xe0, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x1f, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xfe, 0x07, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x3f, 0xc3, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xf8, 0xff, 0xe3, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0xff, 0xf1, 0xff, 0xff, 
0xff, 0xff, 0xe0, 0x01, 0xff, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xc3, 0xfb, 0xff, 0xfc, 0xff, 0xff, 
0xff, 0xff, 0x8f, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0xff, 0x9f, 0xff, 0xff, 0xfc, 0xff, 0xff, 
0xff, 0xff, 0x1f, 0xff, 0xff, 0xfc, 0x7f, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xfc, 0x0f, 0xff, 
0xff, 0xfe, 0x3f, 0xff, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xf8, 0x3f, 0xff, 0xff, 0xff, 0xe3, 0xff, 
0xff, 0xf0, 0x7f, 0xff, 0xff, 0xff, 0xf1, 0xff, 0xff, 0xe3, 0xff, 0xff, 0xff, 0xff, 0xf8, 0xff, 
0xff, 0xc7, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0x8f, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x7f, 
0xff, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x7f, 
0xff, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x7f, 
0xff, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x7f, 
0xff, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x7f, 0xff, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff, 
0xff, 0x8f, 0xff, 0xff, 0xff, 0xff, 0xf8, 0xff, 0xff, 0xc7, 0xff, 0xff, 0xff, 0xff, 0xf1, 0xff, 
0xff, 0xe1, 0xff, 0xff, 0xff, 0xff, 0xc3, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 
0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};

uint8_t cloud14[] = {
// 'cloud14', 64x32px
0xff, 0xff, 0xff, 0xfc, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x03, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xe3, 0xf1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc7, 0xfc, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff, 0x8f, 0xfe, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9f, 0xfe, 0x37, 0xff, 0xff, 
0xff, 0xff, 0xf0, 0x3f, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x3f, 0xff, 0x38, 0x7f, 0xff, 
0xff, 0xff, 0x8f, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0x9f, 0xff, 0xff, 0xff, 0x9f, 0xff, 
0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0x9f, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0xcf, 0xff, 
0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0xcf, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0xcf, 0xff, 
0xff, 0xfe, 0x3f, 0xff, 0xff, 0xff, 0xc7, 0xff, 0xff, 0xfc, 0x7f, 0xff, 0xff, 0xff, 0xe3, 0xff, 
0xff, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xf9, 0xff, 0xff, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff, 
0xff, 0xf3, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xf3, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff, 
0xff, 0xf3, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xf3, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff, 
0xff, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xf8, 0xff, 0xff, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xf1, 0xff, 
0xff, 0xfc, 0x3f, 0xff, 0xff, 0xff, 0x03, 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xff, 0x0f, 0xff, 
0xff, 0xff, 0x9f, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0xff, 0xff, 0xcf, 0xcf, 0xfc, 0x7c, 0x7f, 0xff, 
0xff, 0xff, 0xe0, 0x07, 0xfc, 0x38, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x23, 0xf8, 0x01, 0xff, 0xff, 
0xff, 0xff, 0xff, 0xf0, 0x41, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x07, 0xff, 0xff, 0xff
};

uint8_t balloon48[] = {
// 'balloon48x48', 48x48px
0xff, 0xff, 0xfc, 0x3f, 0xff, 0xff, 0xff, 0xff, 0x80, 0x03, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 
0xff, 0xff, 0xff, 0xfc, 0x1c, 0x38, 0x3f, 0xff, 0xff, 0xf8, 0x1c, 0x39, 0x1f, 0xff, 0xff, 0xf1, 
0x3c, 0x3c, 0x8f, 0xff, 0xff, 0xe2, 0x3c, 0x3c, 0x47, 0xff, 0xff, 0xc6, 0x38, 0x1c, 0x63, 0xff, 
0xff, 0xcc, 0x38, 0x1c, 0x33, 0xff, 0xff, 0x9c, 0x78, 0x1e, 0x39, 0xff, 0xff, 0x9c, 0x78, 0x1e, 
0x39, 0xff, 0xff, 0x38, 0x78, 0x1e, 0x1c, 0xff, 0xff, 0x38, 0x78, 0x1e, 0x1c, 0xff, 0xff, 0x38, 
0x78, 0x1e, 0x1c, 0xff, 0xfe, 0x38, 0x78, 0x1e, 0x1c, 0x7f, 0xfe, 0x78, 0x78, 0x1e, 0x1e, 0x7f, 
0xfe, 0x78, 0x78, 0x1e, 0x1e, 0x7f, 0xfe, 0x78, 0x78, 0x1e, 0x1e, 0x7f, 0xfe, 0x78, 0x78, 0x1e, 
0x1e, 0x7f, 0xfe, 0x78, 0x78, 0x1e, 0x1e, 0x7f, 0xfe, 0x38, 0x78, 0x1e, 0x1c, 0x7f, 0xff, 0x3c, 
0x78, 0x1e, 0x3c, 0x7f, 0xff, 0x3c, 0x78, 0x1e, 0x3c, 0xff, 0xff, 0x1c, 0x78, 0x1e, 0x3c, 0xff, 
0xff, 0x9c, 0x78, 0x1e, 0x39, 0xff, 0xff, 0x8e, 0x78, 0x1e, 0x79, 0xff, 0xff, 0xce, 0x38, 0x1c, 
0x73, 0xff, 0xff, 0xc6, 0x38, 0x1c, 0x63, 0xff, 0xff, 0xe7, 0x3c, 0x3c, 0xe7, 0xff, 0xff, 0xf3, 
0x3c, 0x3c, 0xcf, 0xff, 0xff, 0xf1, 0x9c, 0x39, 0x8f, 0xff, 0xff, 0xf8, 0x9c, 0x39, 0x1f, 0xff, 
0xff, 0xfc, 0xdc, 0x3b, 0x3f, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xfe, 0x00, 0x00, 
0x7f, 0xff, 0xff, 0xff, 0xbb, 0xdd, 0xff, 0xff, 0xff, 0xff, 0xb9, 0x9d, 0xff, 0xff, 0xff, 0xff, 
0x99, 0x99, 0xff, 0xff, 0xff, 0xff, 0xd9, 0xbb, 0xff, 0xff, 0xff, 0xff, 0xcd, 0xb3, 0xff, 0xff, 
0xff, 0xff, 0xc0, 0x03, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x03, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xe7, 
0xff, 0xff, 0xff, 0xff, 0xe7, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xe7, 0xff, 0xff, 0xff, 0xff, 
0xe7, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x07, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x07, 0xff, 0xff
};

uint8_t balloon32[] = {
// 'balloon32x32', 32x32px
0xff, 0xfc, 0x1f, 0xff, 0xff, 0xe0, 0x07, 0xff, 0xff, 0x84, 0x21, 0xff, 0xff, 0x2c, 0x34, 0xff, 
0xfe, 0x4c, 0x32, 0x7f, 0xfe, 0xcc, 0x33, 0x7f, 0xfd, 0x8c, 0x31, 0xbf, 0xf9, 0x9c, 0x39, 0xbf, 
0xf9, 0x9c, 0x39, 0x9f, 0xfb, 0x9c, 0x39, 0xdf, 0xfb, 0x9c, 0x39, 0xdf, 0xfb, 0x9c, 0x39, 0xdf, 
0xfb, 0x9c, 0x39, 0xdf, 0xfb, 0x9c, 0x39, 0xdf, 0xf9, 0x9c, 0x39, 0x9f, 0xfd, 0x9c, 0x39, 0xbf, 
0xfd, 0xcc, 0x33, 0xbf, 0xfc, 0xcc, 0x33, 0x7f, 0xfe, 0x4e, 0x72, 0x7f, 0xff, 0x6e, 0x76, 0xff, 
0xff, 0x2e, 0x74, 0xff, 0xff, 0x96, 0x69, 0xff, 0xff, 0xc0, 0x03, 0xff, 0xff, 0xed, 0xb7, 0xff, 
0xff, 0xfd, 0xbf, 0xff, 0xff, 0xf5, 0xaf, 0xff, 0xff, 0xf0, 0x0f, 0xff, 0xff, 0xf0, 0x0f, 0xff, 
0xff, 0xfb, 0xdf, 0xff, 0xff, 0xfb, 0xdf, 0xff, 0xff, 0xf8, 0x1f, 0xff, 0xff, 0xf8, 0x1f, 0xff
};

#define WITH_BALLOON 1

void app_main(void)
{
	SSD1306_t dev;

#if CONFIG_I2C_INTERFACE
	ESP_LOGI(TAG, "INTERFACE is i2c");
	ESP_LOGI(TAG, "CONFIG_SDA_GPIO=%d",CONFIG_SDA_GPIO);
	ESP_LOGI(TAG, "CONFIG_SCL_GPIO=%d",CONFIG_SCL_GPIO);
	ESP_LOGI(TAG, "CONFIG_RESET_GPIO=%d",CONFIG_RESET_GPIO);
	i2c_master_init(&dev, CONFIG_SDA_GPIO, CONFIG_SCL_GPIO, CONFIG_RESET_GPIO);
#endif // CONFIG_I2C_INTERFACE

#if CONFIG_SPI_INTERFACE
	ESP_LOGI(TAG, "INTERFACE is SPI");
	ESP_LOGI(TAG, "CONFIG_MOSI_GPIO=%d",CONFIG_MOSI_GPIO);
	ESP_LOGI(TAG, "CONFIG_SCLK_GPIO=%d",CONFIG_SCLK_GPIO);
	ESP_LOGI(TAG, "CONFIG_CS_GPIO=%d",CONFIG_CS_GPIO);
	ESP_LOGI(TAG, "CONFIG_DC_GPIO=%d",CONFIG_DC_GPIO);
	ESP_LOGI(TAG, "CONFIG_RESET_GPIO=%d",CONFIG_RESET_GPIO);
	spi_master_init(&dev, CONFIG_MOSI_GPIO, CONFIG_SCLK_GPIO, CONFIG_CS_GPIO, CONFIG_DC_GPIO, CONFIG_RESET_GPIO);
#endif // CONFIG_SPI_INTERFACE

#if CONFIG_FLIP
	dev._flip = true;
	ESP_LOGW(TAG, "Flip upside down");
#endif

#if CONFIG_SSD1306_128x64
	ESP_LOGI(TAG, "Panel is 128x64");
	ssd1306_init(&dev, 128, 64);
#endif // CONFIG_SSD1306_128x64
#if CONFIG_SSD1306_128x32
	ESP_LOGE(TAG, "Panel is 128x32. This demo cannot be run.");
	while(1) { vTaskDelay(1); }
#endif // CONFIG_SSD1306_128x32

	// Allocate memory for work frame
	uint8_t *wk = (uint8_t *)malloc(1024);
	if (wk == NULL) {
		ESP_LOGE(TAG, "malloc failed");
		while(1) { vTaskDelay(1); }
	}

	// Allocate memory for save frame
	uint8_t *save = (uint8_t *)malloc(1024);
	if (save == NULL) {
		ESP_LOGE(TAG, "malloc failed");
		while(1) { vTaskDelay(1); }
	}

	// Allocate memory for background frame
	uint8_t *background = (uint8_t *)malloc(1024*4);
	if (background == NULL) {
		ESP_LOGE(TAG, "malloc failed");
		while(1) { vTaskDelay(1); }
	}

	// Allocate memory for balloon frame
	uint8_t *balloon = (uint8_t *)malloc(1024);
	if (balloon == NULL) {
		ESP_LOGE(TAG, "malloc failed");
		while(1) { vTaskDelay(1); }
	}

	ssd1306_contrast(&dev, 0xff);

	ssd1306_clear_screen(&dev, false);
	ssd1306_bitmaps(&dev, 0, 0, cloud11, 64, 32, true);
	ssd1306_get_buffer(&dev, &background[1024*0]);
	vTaskDelay(10);

	ssd1306_clear_screen(&dev, false);
	ssd1306_bitmaps(&dev, 0, 0, cloud12, 64, 32, true);
	ssd1306_get_buffer(&dev, &background[1024*1]);
	vTaskDelay(10);

	ssd1306_clear_screen(&dev, false);
	ssd1306_bitmaps(&dev, 0, 0, cloud13, 64, 32, true);
	ssd1306_get_buffer(&dev, &background[1024*2]);
	vTaskDelay(10);

	ssd1306_clear_screen(&dev, false);
	ssd1306_bitmaps(&dev, 0, 0, cloud14, 64, 32, true);
	ssd1306_get_buffer(&dev, &background[1024*3]);
	vTaskDelay(10);

	ssd1306_clear_screen(&dev, false);
	ssd1306_bitmaps(&dev, 0, 0, balloon48, 48, 48, true);
	ssd1306_get_buffer(&dev, balloon);
	vTaskDelay(10);

	ssd1306_clear_screen(&dev, false);

	int frameIndex1=0;
	int frameIndex2=3;
	int page1=3;
	int page2=5;
	uint32_t rand = esp_random();
	int segmentOffset1 = (rand % 10);
	int segmentOffset2 = segmentOffset1+64;
	int balloonHorizontalPosition = 40;
	int balloonHorizontalMove = 1;
	while(1) {
		TickType_t startTick = xTaskGetTickCount();
		ssd1306_wrap_arround(&dev, PAGE_SCROLL_DOWN, 0, 0, -1);
		ESP_LOGD(TAG, "frameIndex1=%d page1=%d frameIndex2=%d page2=%d segmentOffset1=%d segmentOffset2=%d",
			frameIndex1, page1, frameIndex2, page2, segmentOffset1, segmentOffset2);

		// Get from internal buffer to local buffer
		// buffer is [8][128] 8 page 128 pixel
		ssd1306_get_buffer(&dev, wk);

		// Set cloud image to page0
		int frameOffset1 = frameIndex1*1024;
		int frameOffset2 = frameIndex2*1024;
		for (int seg=0;seg<128;seg++) {
			wk[seg] = 0;
		}

		if (page1 < 4) {
			for (int seg=0;seg<64;seg++) {
				if (seg+segmentOffset1 > 0 && seg+segmentOffset1 < 128) 
					wk[seg+segmentOffset1] = background[page1*128+seg+frameOffset1];
			}
		}

		if (page2 < 4) {
			for (int seg=0;seg<64;seg++) {
				if (seg+segmentOffset2 > 0 && seg+segmentOffset2 < 128) 
					wk[seg+segmentOffset2] = background[page2*128+seg+frameOffset2];
			}
		}
		ssd1306_set_buffer(&dev, wk);

#if WITH_BALLOON
		// Get frame without balloon
		ssd1306_get_buffer(&dev, save);

		// Set balloon image
		for(int page=1;page<7;page++) {
			for(int seg=0;seg<48;seg++) {
				int bufferIndex = page*128+seg+balloonHorizontalPosition;
				wk[bufferIndex] = balloon[(page-1)*128+seg];
			}
		}
		ssd1306_set_buffer(&dev, wk);
#endif

		// Show internal buffer
		ssd1306_show_buffer(&dev);

#if WITH_BALLOON
		// Set frame without balloon
		ssd1306_set_buffer(&dev, save);
#endif

		// Update balloon horizontal position
		balloonHorizontalPosition = balloonHorizontalPosition + balloonHorizontalMove;
		if (balloonHorizontalPosition > 60) balloonHorizontalMove = -1;
		if (balloonHorizontalPosition < 20) balloonHorizontalMove = +1;

		page1--;
		if (page1 < 0) {
			frameIndex1++;
			if (frameIndex1 > 3) frameIndex1 = 0;
			page1 = 5;
			rand = esp_random();
			segmentOffset1 = (rand % 10) * 4; // 0 to 40
			segmentOffset1 = segmentOffset1 - 20; // -20 to 20
		}
		page2--;
		if (page2 < 0) {
			frameIndex2--;
			if (frameIndex2 < 0) frameIndex2 = 3;
			page2 = 5;
			segmentOffset2 = segmentOffset1+64;
		}
		TickType_t endTick = xTaskGetTickCount();
		ESP_LOGD(TAG, "diffTick=%"PRIu32, endTick - startTick);
		vTaskDelay(10);
	}

}
